import logging
import os
from contextlib import asynccontextmanager

import docker
from docker.errors import DockerException
from fastapi import FastAPI
from starlette.middleware.cors import CORSMiddleware

# 设置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("docker-manager")


# 应用生命周期管理
@asynccontextmanager
async def lifespan(app: FastAPI):
    """管理 Docker 客户端的生命周期"""
    app.state.docker_client = None
    try:
        # 尝试连接 Docker
        client = docker.DockerClient(base_url=os.getenv('DOCKER_HOST', 'unix:///var/run/docker.sock'))
        client.ping()
        app.state.docker_client = client
        logger.info("成功连接到 Docker 守护进程")
    except DockerException as e:
        logger.error(f"无法连接到 Docker 守护进程: {str(e)}")
        raise RuntimeError(f"Docker 连接失败: {str(e)}")

    yield

    if app.state.docker_client:
        app.state.docker_client.close()
        logger.info("Docker 客户端已关闭")


app = FastAPI(lifespan=lifespan)

# 启用CORS，允许任何来源访问以 /api/ 开头的接口
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)


@app.get("/containers")
async def get_containers(all: bool = True):
    """
    获取所有docker容器状态
    :param all: 是否包含已停止的容器（默认只显示运行中的）
    """
    try:
        containers = app.state.docker_client.containers.list(all=all)
        result = []
        for container in containers:
            result.append({
                "id": container.id,
                "name": container.name,
                "status": container.status,
                "image": container.image.tags[0] if container.image.tags else "",
                "created": container.attrs["Created"],
                "ports": container.attrs["NetworkSettings"]["Ports"],
            })
        return {
            "code": 200,
            "msg": "获取容器状态成功",
            "data": result
        }
    except Exception as e:
        return {"error": str(e)}


@app.get("/containers/{container_id}/inspect")
async def get_container_inspect(container_id: str):
    """
    获取指定docker容器详细信息
    :param container_id: 容器ID
    """
    try:
        container = app.state.docker_client.containers.get(container_id)
        inspect = container.attrs
        return {
            "code": 200,
            "msg": "获取容器详细信息成功",
            "data": inspect
        }
    except Exception as e:
        return {"error": str(e)}


@app.get("/containers/{container_id}/logs")
async def get_container_logs(container_id: str):
    """
    获取容器日志
    :param container_id: 容器ID
    :param tail: 返回最后多少行日志 (默认100)
    """
    try:
        container = app.state.docker_client.containers.get(container_id)
        logs = container.logs(tail=None).decode("utf-8")
        return {
            "code": 200,
            "msg": "获取容器日志成功",
            "data": logs
        }
    except Exception as e:
        return {"error": str(e)}


@app.post("/containers/{container_id}/start")
async def start_container(container_id: str):
    """
    启动指定docker容器
    :param container_id: 容器ID
    """
    try:
        container = app.state.docker_client.containers.get(container_id)
        container.start()
        return {
            "code": 200,
            "msg": "容器启动成功" + container_id,
            "data": container_id
        }
    except Exception as e:
        return {"error": str(e)}


@app.post("/containers/{container_id}/stop")
async def stop_container(container_id: str):
    """
    停止指定docker容器
    :param container_id: 容器ID
    """
    try:
        container = app.state.docker_client.containers.get(container_id)
        container.stop()
        return {
            "code": 200,
            "msg": "容器停止成功" + container_id,
            "data": container_id
        }
    except Exception as e:
        return {"error": str(e)}


@app.post("/containers/{container_id}/restart")
async def restart_container(container_id: str):
    """
    重启指定docker容器
    :param container_id:
    :return:
    """
    try:
        container = app.state.docker_client.containers.get(container_id)
        container.restart()
        return {
            "code": 200,
            "msg": "容器重启成功" + container_id,
            "data": container_id
        }
    except Exception as e:
        return {"error": str(e)}


if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=16018)
